package com.crawler.clent;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.core.utils.ValidateUtil;
import com.crawler.entity.CrawlerLog;
import com.crawler.parser.ParserKit;
import com.crawler.pojo.Constant;
import com.crawler.pojo.PageType;
import com.crawler.pojo.Proxy;
import com.google.common.collect.Maps;

public abstract class CrawlerAdapter implements Crawler{
	
	private Logger logger = LoggerFactory.getLogger(CrawlerAdapter.class);
	
	private CloseableHttpClient client;
	private String loginName;
	private String password;
	private int repeatTimes = 5;
	
	protected Map<PageType, String> cachePage = Maps.newHashMap();
	
	protected CrawlerAdapter(){
		
	}
	
	protected CrawlerAdapter(Proxy proxy){
		if(this.getClass().getSuperclass().getSimpleName().contains("CrawlerAdapter")){
			try {
				this.initClient(proxy);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	protected void initClient(Proxy proxy) throws Exception {
		
		SSLContext sslcontext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }
        }).build();
		
		X509HostnameVerifier hostnameVerifier = new BrowserCompatHostnameVerifier();
		Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
				.<ConnectionSocketFactory> create()
				.register("http", PlainConnectionSocketFactory.INSTANCE)
				.register("https",new SSLConnectionSocketFactory(sslcontext,hostnameVerifier)).build();

		PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
		connManager.setMaxTotal(1100); //设置整个连接池最大连接数 根据自己的场景决定
        connManager.setDefaultMaxPerRoute(100);
        
        CookieStore cookieStore = new BasicCookieStore();
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        
        RequestConfig defaultRequestConfig = RequestConfig.custom()
                							.setCookieSpec(CookieSpecs.BROWSER_COMPATIBILITY)
//	                						.setCookieSpec(CookieSpecs.BEST_MATCH)
							                .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
							                .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
							                .build();
        // 设置代理
		if(proxy != null){
			HttpHost proxyHost = new HttpHost(proxy.getIp(),Integer.parseInt(proxy.getPort()));
			if(ValidateUtil.isValid(proxy.getUsername(),proxy.getPassword())){
				credentialsProvider.setCredentials(
		                new AuthScope(proxy.getIp(), Integer.parseInt(proxy.getPort())),
		                new UsernamePasswordCredentials(proxy.getUsername(), proxy.getPassword()));
			}
			DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxyHost);
			client = HttpClients.custom()
		            .setConnectionManager(connManager)
		            .setDefaultCookieStore(cookieStore)
		            .setDefaultCredentialsProvider(credentialsProvider)
		            .setRoutePlanner(routePlanner)
		            .setDefaultRequestConfig(defaultRequestConfig).build();
		} else {
			client = HttpClients.custom()
	            .setConnectionManager(connManager)
	            .setDefaultCookieStore(cookieStore)
	            .setDefaultCredentialsProvider(credentialsProvider)
	            .setDefaultRequestConfig(defaultRequestConfig).build();
		}
		
		RequestConfig.copy(defaultRequestConfig)
                	 .setSocketTimeout(60000)
                	 .setConnectTimeout(10000)
                	 .setConnectionRequestTimeout(5000)
                	 .build();
		logger.info("client init success...");
	}
	
	protected String requestGet(String url,List<Header> headers){
		HttpGet httpGet = new HttpGet(url);
		if(headers != null){
			httpGet.setHeaders(headers.toArray(new Header[0]));
		}
		return responseProcessor(requestProcessor(getClient(),httpGet),url);
	}
	
	protected String requestPost(String url,List<NameValuePair> params,List<Header> headers){
		HttpPost httpPost = null;
		try {
			httpPost = new HttpPost(url);
			if(params != null){
				HttpEntity httpEntity = new UrlEncodedFormEntity(params);
				httpPost.setEntity(httpEntity);
			}
			if(headers != null){
				httpPost.setHeaders(headers.toArray(new Header[0]));
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return responseProcessor(requestProcessor(getClient(),httpPost),url);
	}
	
	protected CloseableHttpResponse requestProcessor(CloseableHttpClient client, HttpRequestBase httpRequest){
		CloseableHttpResponse response = null;
		try {
			for (int i = 0; i < repeatTimes; i++) {
				try {
					response = client.execute(httpRequest);
					break;
				} catch (SocketTimeoutException e) {
					if(i == repeatTimes-1){
						throw e;
					}
					i++;
				}catch(ConnectTimeoutException ee){
					if(i == repeatTimes-1){
						throw ee;
					}
					i++;
				}
			}
			CrawlerLog.me.save(Constant.请求,getLoginName(),getPassword(),httpRequest,Constant.SUCCESS,null,null);
		}catch (Exception e) {
			CrawlerLog.me.save(Constant.请求,getLoginName(),getPassword(),httpRequest,Constant.FAILURE,ParserKit.getExceptionMsg(e),null);
		}
		return response;
	}

	protected String responseProcessor(CloseableHttpResponse resp,String url){
		String content = "" ;
		try {
			content = EntityUtils.toString(resp.getEntity(),"gb2312");
			EntityUtils.consume(resp.getEntity());
			CrawlerLog.me.save(Constant.响应,getLoginName(),getPassword(),url,Constant.SUCCESS,null,content);
		} catch (ParseException e) {
			CrawlerLog.me.save(Constant.响应,getLoginName(),getPassword(),url,Constant.FAILURE,ParserKit.getExceptionMsg(e),content);
		} catch (IOException e) {
			CrawlerLog.me.save(Constant.响应,getLoginName(),getPassword(),url,Constant.FAILURE,ParserKit.getExceptionMsg(e),content);
		}finally {
			if (resp != null) {
				try {
					resp.close();
				} catch (IOException e) {
					CrawlerLog.me.save(Constant.响应,getLoginName(),getPassword(),url,Constant.FAILURE,ParserKit.getExceptionMsg(e),content);
				}
			}
		}
		return content;
	}
	
	protected byte[] getByteArray(CloseableHttpResponse resp,String url){
		byte[] b = null;
		try {
			b = EntityUtils.toByteArray(resp.getEntity());
			EntityUtils.consume(resp.getEntity());
			CrawlerLog.me.save(Constant.响应,getLoginName(),getPassword(),url,Constant.SUCCESS,null,null);
		} catch (IOException e) {
			CrawlerLog.me.save(Constant.响应,getLoginName(),getPassword(),url,Constant.FAILURE,ParserKit.getExceptionMsg(e),null);
		}finally {
			if (resp != null) {
				try {
					resp.close();
				} catch (IOException e) {
					CrawlerLog.me.save(Constant.响应,getLoginName(),getPassword(),url,Constant.FAILURE,ParserKit.getExceptionMsg(e),null);
				}
			}
		}
		return b;
	}

	public CloseableHttpClient getClient() {
		return client;
	}

	public void setClient(CloseableHttpClient client) {
		this.client = client;
	}

	public String getLoginName() {
		return loginName;
	}

	public void setLoginName(String loginName) {
		this.loginName = loginName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
}
